package com.us.util;

import java.io.File;
import java.io.FileFilter;
import java.lang.annotation.Annotation;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * 对类的一些操作
 * 
 * @author monlyu
 */
public class ClassUtil {

    /**
     * 获取setter方法名<br>
     * <code>inPut=>id</code> <code>outPut=>getId</code>
     */
    public static String getter(String field) {
        return "get" + StringUtil.capFisrtChar(field);
    }

    public static String bolGetter(String field) {
        return "is" + StringUtil.capFisrtChar(field);
    }

    /**
     * 获取set的方法名
     */
    public static String setter(String field) {
        return "get" + StringUtil.capFisrtChar(field);
    }

    /**
     * 根据字符串找到某个类中某个类型<br>
     * 如果没有 则返回<code>null</code>
     * 
     * @since 2009-12-28
     */
    public static Class<?> getFiledType(Object object, String field) {
        Class<?> cls = null;
        try {
            cls = object.getClass().getMethod(ClassUtil.getter(field)).getReturnType();
        } catch (Exception e) {
            try {
                cls = object.getClass().getMethod(ClassUtil.bolGetter(field)).getReturnType();
            } catch (Exception e1) {
                UsLogUtil.getLog(ClassUtil.class).error("查找字段类型失败:" + e);
            }
        }
        return cls;
    }

    /**
     * 是否为默认包
     * 
     * @since 2009-12-28
     */
    public static boolean isInDefVoPackage(Class<?> clazz) {
        return isInPackage(clazz, "com.us.vo");
    }

    /**
     * 判断类 clazz 是否在 package 中
     * 
     * @since 2009-12-30
     */
    public static boolean isInPackage(Class<?> clazz, String packname) {
        return clazz.getPackage().getName().equals(packname);
    }

    /**
     * 获取某个类
     * 
     * @since 2010-1-1
     */
    public static Class<?> getClass(Object inObj) {
        final Class<?> clazz = inObj.getClass();
        return clazz.getName().indexOf("$") > 0 ? clazz.getSuperclass() : clazz;
    }

    /**
     * 获取某个包下面的所有类
     * 
     * @since 2010-1-8
     * @param packname
     *        包名 如：com.monlyu.vo
     * @param recursive
     *        是否进行迭代
     * @return 某个包内的所有类的实例
     */
    public static Set<Class<?>> getClassesByPackage(String packname, boolean recursive) {
        if (StringUtil.isEmpty(packname)) {
            return null;
        }
        Set<Class<?>> classes = new LinkedHashSet<Class<?>>();
        Enumeration<URL> dirs;
        try {
            String packdir = packname.replace('.', '/');
            dirs = Thread.currentThread().getContextClassLoader().getResources(packdir);
            while (dirs.hasMoreElements()) {
                URL url = dirs.nextElement();
                if (url.getProtocol().equals("file")) {
                    String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
                    addFileClass2Set(packname, filePath, recursive, classes);
                } else if (url.getProtocol().equals("jar")) {
                    JarFile jar = ((JarURLConnection) url.openConnection()).getJarFile();
                    addJarClass2Set(packname, recursive, classes, packdir, jar);
                }
            }
        } catch (Exception e) {
            UsLogUtil.getLog(ClassUtil.class).error("获取类列表的时候出错" + e);
        }
        return classes;
    }

    /**
     * 把jar包里面还有你需要的目录添加到你的的set中去
     * 
     * @param packname
     *        包名
     * @param recursive
     *        是否进行迭代
     * @param classes
     *        输入输出的类
     * @param packdir
     *        包的目录`
     * @param jar
     *        jar文件
     * @return
     */
    private static void addJarClass2Set(String packname, boolean recursive, Set<Class<?>> classes, String packdir, JarFile jar) {
        while (jar.entries().hasMoreElements()) {
            JarEntry jarEntry = jar.entries().nextElement();
            String name = jarEntry.getName();
            if (name.charAt(0) == '/') {
                name = name.substring(1);
            }
            if (name.startsWith(packdir)) {
                int idx = name.lastIndexOf('/');
                if (idx == -1) {
                    packname = name.substring(0, idx).replace('/', '.');
                }
                if (idx != -1 || recursive) {
                    if (name.endsWith(".class") && !jarEntry.isDirectory()) {
                        String className = name.substring(packname.length() + 1, name.length() - 6);
                        try {
                            classes.add(Class.forName(packname + '.' + className));
                        } catch (ClassNotFoundException e) {
                            UsLogUtil.getLog(ClassUtil.class).warn("找不到:" + className);
                        }
                    }
                }
            }
        }
    }

    /**
     * 添加文件Class到Set里面去 添加磁盘上单独的class文件进入Set列表
     */
    private static void addFileClass2Set(String packname, String packPath, final boolean recursive, Set<Class<?>> classes) {
        File dir = new File(packPath);
        if (!dir.exists() || !dir.isDirectory()) {
            UsLogUtil.getLog(ClassUtil.class).error("该包" + packname + "下无任何文件");
            return;
        }
        File[] files = dir.listFiles(new FileFilter() {
            @Override
            public boolean accept(File file) {
                return (recursive && file.isDirectory()) || (file.getName().endsWith(".class"));
            }
        });
        for (File file : files) {
            if (file.isDirectory()) {
                addFileClass2Set(packname + "." + file.getName(), file.getAbsolutePath(), recursive, classes);
            } else {
                String className = file.getName().substring(0, file.getName().length() - 6);
                try {
                    classes.add(Class.forName(packname + '.' + className));
                } catch (ClassNotFoundException e) {
                    UsLogUtil.getLog(ClassUtil.class).warn("类文件尚未找到" + className);
                }
            }
        }
    }

    public static Set<Class<?>> getClassWithAnnoByPackage(Class<? extends Annotation> clazz, String packname) {
        Set<Class<?>> set = new LinkedHashSet<Class<?>>();
        for (Class<?> classinSet : getClassesByPackage(packname, false)) {
            if (classinSet.getAnnotation(clazz) != null) {
                classinSet.getName();
                set.add(classinSet);
            }
        }
        return set;
    }
}